/* Copyright (C) 2010 Fredrique Samuels, fredriquesamuels@gmail.com */

/*
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "a_entity.h"

#include "graphics.h"

#include <iostream>

namespace tri {

unsigned int abstract_entity::entity_count = 0;

camera* abstract_entity::mActiveCamera = NULL;

camera* abstract_entity::getActiveCamera(){
	return mActiveCamera;
}

void abstract_entity::setActiveCamera(camera* cam){
	mActiveCamera = cam;
}
	


abstract_entity::abstract_entity() :
    node::Node(), tri::events::event_entity(), load_name(true),
            renderchildren(true) ,mWireFrameMode(false),mRepaintRequest(true),
			mVisible(true){
    this->entity_id = ++abstract_entity::entity_count;
    this->setSize(1, 1, 1);
    setScale(1,1,1);
}

abstract_entity::~abstract_entity() {
}

void abstract_entity::enableRecursiveRender(bool b) {
    renderchildren = b;
    requestRepaint();
}

void abstract_entity::update() {
    if (renderchildren) {
        update_children();
        update_next();
    }

}

void abstract_entity::setVisualRotation(tri::vector v, float t){
	if(t<=0){
		mVisualRotation.setValue(v);
	}else{
		mVisualRotation.setTargetValue(v.x,v.y,v.z,t);
	}
}


bool abstract_entity::loadName() {
    return load_name;
}

void abstract_entity::setLoadName(bool b){
	load_name = b;
}

void abstract_entity::secondaryDisplay(){}

void abstract_entity::render() {
    if(mVisible){
	
		if (load_name) {
			graphics::loadname(this->getEntityID());
		}
		graphics::push_matrix();
		applyTranformation();
		camera* cam = getActiveCamera();
		if (cam!=NULL){
			savePosition(cam->getGlobalPosition(getAbsolutePosition()));
		}
		tri::vector _scale = getScale();
		glScaled(_scale.x,_scale.y,_scale.z);
		if(mWireFrameMode){
			glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
		}
		glPushMatrix();
		graphics::rotate(mVisualRotation);
		this->display();
		glPopMatrix();
		glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
		glPushMatrix();
		this->secondaryDisplay();
		glPopMatrix();
		mRepaintRequest = false;
		render_children();
		graphics::pop_matrix();
	}

    if (renderchildren) {
        render_next();
    }
}

void abstract_entity::render_children() {
    Node* cn = this->getChildNode();
    if (cn != NULL) {
        ((abstract_entity*)cn)->render();
    }
}

void abstract_entity::update_children() {
    Node* cn = this->getChildNode();
    if (cn != NULL) {
        ((abstract_entity*)cn)->update();
    }
}

void abstract_entity::render_next() {
    Node* n = this->getNextNode();
    if (n != NULL) {
        ((abstract_entity*)n)->render();
    }
}

void abstract_entity::update_next() {
    Node* n = this->getNextNode();
    if (n != NULL) {
        ((abstract_entity*)n)->update();
    }
}

bool abstract_entity::repaintQuestedByChildren(){
	bool ret =false;
	Node* cn = this->getChildNode();
    if (cn != NULL) {
        ret = ((abstract_entity*)cn)->repaintRequested();
    }
	return ret;
}

bool abstract_entity::repaintQuestedByNext(){
	bool ret =false;
	Node* n = this->getNextNode();
    if (n != NULL) {
        ret = ((abstract_entity*)n)->repaintRequested();
    }
	return ret;
}

void abstract_entity::requestRepaint(){
    mRepaintRequest = true;
}

bool abstract_entity::repaintRequested(){
	bool ret = mRepaintRequest or pos.isAnimating() or size.isAnimating() or rot.isAnimating();
	if (!ret){
		ret = repaintQuestedByChildren();
		if (!ret){
			ret = repaintQuestedByNext();
		}
	}
	
	return ret;
}

unsigned int abstract_entity::getEntityID() {
    return entity_id;
}

void abstract_entity::setPosition(tri::vector v){	
	pos.setValue(v.x, v.y, v.z);
    requestRepaint();
}

void abstract_entity::setRotation(tri::vector v){	
	rot.setValue(v.x, v.y, v.z);
    requestRepaint();
}

void abstract_entity::setSize(tri::vector v){	
	size.setValue(v.x, v.y, v.z);
    requestRepaint();
}


void abstract_entity::setPosition(float x, float y, float z) {
    pos.setValue(x, y, z);
    requestRepaint();
}

void abstract_entity::setRotation(float x, float y, float z) {
    rot.setValue(x, y, z);
    requestRepaint();
}

void abstract_entity::getPosition(float *v) {
    pos.getValue(v);
}

tri::vector abstract_entity::getRotation(){
	float v[3];
	getRotation(v);
	return tri::vector(v[0],v[1],v[2]);
}

tri::vector abstract_entity::getPosition(){
	float v[3];
	getPosition(v);
	return tri::vector(v[0],v[1],v[2]);
}

tri::vector abstract_entity::getSize(){
	float v[3];
	getSize(v);
	return tri::vector(v[0],v[1],v[2]);
}

void abstract_entity::getRotation(float *v) {
    rot.getValue(v);
}

void abstract_entity::setTargetRotation(tri::vector v,float t){
	setTargetRotation(v.x,v.y,v.z,t);
}

void abstract_entity::setTargetPosition(tri::vector v,float t){
	setTargetPosition(v.x,v.y,v.z,t);
}

void abstract_entity::setTargetPosition(float x, float y, float z, float t) {
    pos.setTargetValue(x, y, z, t);
    requestRepaint();
}
void abstract_entity::setTargetRotation(float x, float y, float z, float t) {
    rot.setTargetValue(x, y, z, t);
    requestRepaint();
}

void abstract_entity::setTargetSize(float x, float y, float z, float t) {
    size.setTargetValue(x, y, z, t);
    requestRepaint();
}

void abstract_entity::setScale(tri::vector v){
	scale.setValue(v);
	requestRepaint();
}

void abstract_entity::setScale(float x, float y, float z){
	scale.setValue(x,y,z);
	requestRepaint();
}

tri::vector abstract_entity::getScale(){
	return scale.getValue();
}

tri::vector abstract_entity::getPrevPosition(){
	return mPrevPos;
}

void abstract_entity::savePosition(tri::vector v){
	mPrevPos = v;
}

void abstract_entity::applyTranformation() {
    graphics::translate(pos);
    graphics::rotate(rot);
    glGetFloatv(GL_MODELVIEW_MATRIX,model_view_matrix.elements);
}

void abstract_entity::get_model_matrix(tri::matrix* m) {
    for (int i = 0; i < 16; i++) {
        m->elements[i] = model_view_matrix.elements[i];
    }
}

void abstract_entity::updateModelViewMatrix(){
	graphics::push_matrix();
	applyTranformation();
	abstract_entity* cn = (abstract_entity*)this->getChildNode();
	if(cn!=NULL){
		cn->updateModelViewMatrix();
	}
	graphics::pop_matrix();
	abstract_entity* n = (abstract_entity*)this->getNextNode();
    if (n!=NULL) {
    	n->updateModelViewMatrix();
    }
}

tri::vector abstract_entity::getAbsolutePosition(){
	return tri::vector(model_view_matrix.elements[12],model_view_matrix.elements[13],model_view_matrix.elements[14]);
}

anivector* abstract_entity::getPositionVector() {
    return &pos;
}
anivector* abstract_entity::getRotationVector() {
    return &rot;
}

void abstract_entity::addEntity(abstract_entity* e) {
    addNode(e);
    requestRepaint();
}

void abstract_entity::setSize(float x, float y, float z) {
    size.setValue(x, y, z);
    requestRepaint();
}
void abstract_entity::setSize(float *v) {
    size.setValue(v[0], v[1], v[2]);
    requestRepaint();
}
void abstract_entity::getSize(float *v) {
    size.getValue(v);
}
void abstract_entity::setName(std::string name) {
    this->name = name;
    requestRepaint();
}

std::string abstract_entity::getName() {
    return name;
}

abstract_entity* abstract_entity::getByEntityID(unsigned int id) {
    if (id == this->getEntityID()) {
        return this;
    }

    Node* cn = this->getChildNode();
    Node* n= NULL;
    while (cn!=NULL) {
        n = ((abstract_entity*)cn)->getByEntityID(id);
        if (n!=NULL) {
            break;
        }
        cn = cn->getNextNode();
    }
    return ((abstract_entity*)n);

}


void abstract_entity::setWireFrame(bool b){
    mWireFrameMode = b;
    requestRepaint();
}

void abstract_entity::setVisible(bool b){
	mVisible=b;
}

bool abstract_entity::isVisible(){
	return mVisible;
}


//=============================================================

void abstract_entity::setColor(int r, int g, int b, int a) {
	mMaterial.setColor(r,g,b,a);
}

void abstract_entity::setTargetColor(int r, int g, int b, int a, float t) {
	mMaterial.setTargetColor(tri::graphics::RGBA(r, g, b, a), t);
}

tri::graphics::color abstract_entity::getColor(){
	return mMaterial.getColor();
}

graphics::RGBA abstract_entity::getColorAsRGBA(){
	return mMaterial.getColorAsRGBA();
}

void abstract_entity::applyColor(){
	mMaterial.getColor().apply();
}

void abstract_entity::setColor(graphics::RGBA c){
	mMaterial.setColor(c);
}

void abstract_entity::setTargetColor(graphics::RGBA c,float t){
	mMaterial.setTargetColor(c,t);
}

void abstract_entity::setImage(std::string image,bool filter,bool mipmap) {
	mMaterial.setImage(image,filter,mipmap);
}

std::string abstract_entity::getImage() {
	return mMaterial.getImage();
}

bool abstract_entity::isImageFiltered(){
	return mMaterial.isImageFiltered();
}

bool abstract_entity::isImageMipmap(){
	return mMaterial.isImageMipmap();
}

tri::texture* abstract_entity::getTexture(){
	return mMaterial.getTexture();
}

void abstract_entity::setMaterial(std::string mat){
	mMaterialPointer = mat;
}

std::string abstract_entity::getMaterial(){
	return mMaterialPointer;
}

Material* abstract_entity::getMaterialPointer(){
	Material* mat = Material::getMaterial(mMaterialPointer);
	if (mat!=NULL){
		return mat;
	}
	return &mMaterial;
}

}
